//
//  SLDownloadTaskManager.m
//  Objective-C
//
//  Created by S_LINE on 2019/9/7.
//  Copyright © 2019 S_Line. All rights reserved.
//

#import "SLDownloadTaskManager.h"

static NSString *backgroundSessionIdentify = @"com.sline.bgs.sldownload";

@interface SLDownloadTaskManager ()<NSURLSessionDelegate, NSURLSessionDownloadDelegate>

@property (nonatomic, strong) NSURLSession *session;

@property (nonatomic, strong) NSMutableDictionary *downloadTaskDict;

@end

@implementation SLDownloadTaskManager

+ (instancetype)shareInstance {
    static SLDownloadTaskManager *staticDownloadTaskManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (staticDownloadTaskManager == nil) {
            staticDownloadTaskManager = [[SLDownloadTaskManager alloc] init];
        }
    });
    
    return staticDownloadTaskManager;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        [self initData];
    }
    return self;
}

- (void)initData {
    self.downloadTaskDict = [NSMutableDictionary new];
    
    [self resetWithBackgroundSesstion];
}

- (void)resetWithBackgroundSesstion {
    NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:backgroundSessionIdentify];
    sessionConfig.allowsCellularAccess = YES;
    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    self.session = session;
}

- (void)startDownloadWithFileModel:(SLDownloadFileModel *)fileModel {
    NSURL *url = [NSURL URLWithString:fileModel.downloadUrl];
    NSURLRequest *downloadRequest = [NSURLRequest requestWithURL:url];
    NSURLSessionDownloadTask *downloadTask = nil;
    
    if (fileModel.downloadTask) {
        [fileModel.downloadTask cancel];
        fileModel.downloadTask = nil;
    }
    
    if (fileModel.resumeData) {
        downloadTask = [self.session downloadTaskWithResumeData:fileModel.resumeData];
        fileModel.resumeData = nil;
        [fileModel saveData];
        if (!downloadTask) {
            downloadTask = [self downloadTaskWithRequest:downloadRequest];
        }
    }
    else {
        downloadTask = [self downloadTaskWithRequest:downloadRequest];
    }
    fileModel.downloadTask = downloadTask;
    
    [downloadTask resume];
    fileModel.downloadStateEnum = SLDownloadStateDownloading;
    
    [self.downloadTaskDict setObject:fileModel forKey:downloadTask];
}

- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request {
    NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request];
    
    return downloadTask;
}

- (void)pauseDownloadWithFileModel:(SLDownloadFileModel *)fileModel toDownloadStateEnum:(SLDownloadState)downloadStateEnum {
    NSURLSessionDownloadTask *downloadTask = fileModel.downloadTask;
    if (downloadTask) {
        if (downloadTask.state == NSURLSessionTaskStateRunning) {
            [downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
                fileModel.resumeData = resumeData;
                fileModel.downloadStateEnum = SLDownloadStatePaused;
                [fileModel saveData];
            }];
        }
    }
}

#pragma mark - NSURLSessionDelegate
/* The last message a session receives.  A session will only become
 * invalid because of a systemic error or when it has been
 * explicitly invalidated, in which case the error parameter will be nil.
 */
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error {
    NSLog(@"%@", error);
}

#pragma mark - NSURLSessionDownloadDelegate
/* Sent when a download task that has completed a download.  The delegate should
 * copy or move the file at the given location to a new location as it will be
 * removed when the delegate message returns. URLSession:task:didCompleteWithError: will
 * still be called.
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
    
}

/* Sent periodically to notify the delegate of download progress. */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
    NSProgress *progress = [NSProgress progressWithTotalUnitCount:totalBytesExpectedToWrite];
    progress.completedUnitCount = totalBytesWritten;
    
    NSLog(@"🍏🍏🍏🍏🍏🍏🍏🍏 downloadTask.currentRequest.URL.absoluteString:%@ - totalBytesWritten：%lld - totalBytesExpectedToWrite:%lld", downloadTask.currentRequest.URL.absoluteString, totalBytesWritten, totalBytesExpectedToWrite);
    
    SLDownloadFileModel *fileModel = [self.downloadTaskDict objectForKey:downloadTask];
    if ([fileModel isKindOfClass:[SLDownloadFileModel class]]) {
        NSDecimalNumber *totalBytesWrittenDecimalNumber = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%lld", totalBytesWritten]];
        NSDecimalNumber *totalBytesExpectedToWriteDecimalNumber = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%lld", totalBytesExpectedToWrite]];
        fileModel.progress = [totalBytesWrittenDecimalNumber decimalNumberByDividingBy:totalBytesExpectedToWriteDecimalNumber withBehavior:[self decimalNumberHandler]];
    }
    else {
        [downloadTask cancel];
    }
}

- (NSDecimalNumberHandler *)decimalNumberHandler {
    NSDecimalNumberHandler *decimalNumberHandler = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:2 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
    return decimalNumberHandler;
}

/* Sent when a download has been resumed. If a download failed with an
 * error, the -userInfo dictionary of the error will contain an
 * NSURLSessionDownloadTaskResumeData key, whose value is the resume
 * data.
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
    
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionDownloadTask *)downloadTask didCompleteWithError:(NSError *)error {
    NSLog(@"%@",error);
}

@end
